package com.ys.fmms.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

@Controller
public class DownloadController {
    private final static String utf8 = "utf-8";

    @RequestMapping("/download")
    public void downLoadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        File file = new File("D:\\studyMaterials\\计算机基础\\操作系统\\操作系统新课\\第三章 内存管理\\第三章 内存管理1.mp4");
        response.setCharacterEncoding(utf8);
        InputStream is = null;
        OutputStream os = null;
        try {
            // 分片下载 http Range bytes=1-100 bytes=100-
            long fSize = file.length();
            // 指定内容类型
            response.setContentType("application/x-download");
            // 解决字符编码问题
            String fileName = URLEncoder.encode(file.getName(), utf8);
            // 设置前端以下载的方式打开默认的弹框
            response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
            // 是否需要分片下载支持
            response.setHeader("Accept-Range", "bytes");
            response.setHeader("fSize", String.valueOf(fSize));
            response.setHeader("fName", fileName);

            // pos 文件起始大小 last：要下载的文件总大小 sum：经过多少次下载
            long pos = 0, last = fSize - 1, sum = 0;
            // 为空说明当前客户端不要求切片下载
            if (null != request.getHeader("Range")) {
                // 设置分片下载 http请求规定
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                // bytes=1-100 bytes=100-
                String numRange = request.getHeader("Range").replaceAll("bytes=", "");
                String[] strRange = numRange.split("-");
                if (strRange.length == 2) {
                    pos = Long.parseLong(strRange[0].trim());
                    last = Long.parseLong(strRange[1].trim());
                    // 因为这里使用了切片下载，在客户端进行分片时候切片范围是固定的
                    // 最后一个切片数量可能会大于实际的字节总数，所以此时最多字节数就是文件总大小-1
                    if (last > fSize - 1) {
                        last = fSize - 1;
                    }
                } else {
                    pos = Long.parseLong(numRange.replaceAll("-", "").trim());
                }
            }
            // 如果是切片文件的话rangeLength 为当前切片总字节数 否则是当前总文件字节数 当前的切片文件
            long rangeLength = last - pos + 1;
            // bytes 0-157638235/文件的总字节数 当前文件/切片文件的字节数量范围
            String contentRange = new StringBuffer("bytes ").append(pos).append("-").append(last).append("/").append(rangeLength).toString();
            // 在请求头中设置当前文件的字节数范围 bytes 当前切片的字节数/文件最终的字节数 0-255/256
            response.setHeader("Content-Range", contentRange);
            // 在请求头中设置当前文件的总字节数 157638236
            response.setHeader("Content-Length", String.valueOf(rangeLength));

            // 创建输入输出流进行文件的读写
            os = new BufferedOutputStream(response.getOutputStream());
            is = new BufferedInputStream(new FileInputStream(file));
            // 此处还可以使用RandomAccessFile 这里skip中的数字 表示待跳过的字节数量
            // 跳过是因为如果采用分片下载下一次的字节数量的开始应该是新的start
            is.skip(pos);
            // 定义每次读取的字节数 相当于缓冲区
            byte[] buffer = new byte[1024];// 这里是字节数
            int length = 0;
            // 当前读取的总字节数sum
            while (sum < rangeLength) {
                // 当前已经读取的文件字节数 如果出现最后一次读取的字节数少于设定的每次读取的字节那么最多读取的字节数即为小的
                // 通过当前读取的总数sum 和 文件总数只差小于每次最多读取的数 buffer 则是小的否则是默认buffer
                // sum 已经读取的字节数  和 待读取的总字节数
                length = is.read(buffer, 0, (rangeLength - sum) <= buffer.length ? (int) (rangeLength - sum) : buffer.length);
                sum = sum + length;
                os.write(buffer, 0, length);
            }
            System.out.println("下载完成");
        } finally {
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
    }
}
